/*
 * SHOPTNT 版权所有。
 * 未经许可，您不得使用此文件。
 * 官方地址：www.shoptnt.cn
 */
package cn.shoptnt.model.support.validator.validator;

import cn.shoptnt.framework.context.user.AdminUserContext;
import cn.shoptnt.framework.context.user.UserContext;
import cn.shoptnt.framework.rabbitmq.MessageSender;
import cn.shoptnt.framework.rabbitmq.MqMessage;
import cn.shoptnt.framework.security.model.Admin;
import cn.shoptnt.framework.security.model.Seller;
import cn.shoptnt.framework.util.DateUtil;
import cn.shoptnt.framework.util.StringUtil;
import cn.shoptnt.model.base.rabbitmq.AmqpExchange;
import cn.shoptnt.model.support.LogClient;
import cn.shoptnt.model.support.validator.annotation.Log;
import cn.shoptnt.model.support.validator.annotation.LogLevel;
import cn.shoptnt.model.system.dos.SystemLogs;
import net.sf.json.JSONObject;
import org.apache.commons.lang.text.StrSubstitutor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * 记录操作日志切面类
 *
 * @author fk
 * @version v1.0
 * @since v6.2
 * 2016年12月7日 下午1:19:46
 */
@Component
@Aspect
public class LogAspect {


    @Autowired
    private MessageSender messageSender;

    /**
     * 切面方法
     *
     * @param point 切点
     * @param log   注解log对象
     * @throws Exception
     */
    @AfterReturning("@annotation(log)")
    public void doAfter(JoinPoint point, Log log) throws Exception {
        //所属端
        LogClient client = log.client();
        //操作描述
        String detail = log.detail();
        //日志级别
        LogLevel level = log.level();

        //连接点捕获的代码块
        CodeSignature signature = (CodeSignature) point.getSignature();

        Object[] args = point.getArgs();
        //获取注解中替换后的内容
        detail = this.getDetail(detail,signature,args);

        String operatorName = "";
        Long operatorId = 0L;
        Long sellerId = 0L;

        //管理端操作
        if ("admin".equals(client.name())) {
            Admin admin = AdminUserContext.getAdmin();
            operatorId = admin.getUid();
            operatorName = admin.getUsername();
        }
        //商家端操作
        if ("seller".equals(client.name())) {
            Seller seller = UserContext.getSeller();
            operatorId = seller.getUid();
            sellerId = seller.getSellerId();
            operatorName = seller.getUsername();
        }

        //调用的方法类名+方法名如cn.shoptnt.api.manager.goods.GoodsManagerController.list()
        String method = signature.getDeclaringTypeName() + "." + signature.getName() + "()";

        SystemLogs systemLogs = new SystemLogs();
        systemLogs.setOperateDetail(detail);
        systemLogs.setOperateIp(StringUtil.getIpAddress());
        systemLogs.setParams(StringUtil.arrayToString(args,"&"));
        systemLogs.setMethod(method);
        systemLogs.setOperateTime(DateUtil.getDateline());
        systemLogs.setOperatorId(operatorId);
        systemLogs.setOperatorName(operatorName);
        systemLogs.setSellerId(sellerId);
        systemLogs.setLevel(level.name());
        systemLogs.setClient(client.name());

		this.messageSender.send(new MqMessage(AmqpExchange.LOGS, "LOGS_ROUTINGKEY", systemLogs));
    }

    /**
     * 得到具体的操作内容
     * @param detail 注解中的内容
     * @param signature point连接点捕获的代码块
     * @param args point得到的注解方法上的所有参数 数组
     * @return
     */
    private String getDetail(String detail, CodeSignature signature, Object[] args) {

        Map valuesMap = new HashMap();
        // 找出所有表达式
        String p = "\\$\\{(.*?)\\}";
        Pattern pattern = Pattern.compile(p);
        Matcher matcher = pattern.matcher(detail);

        //匹配${}
        while (matcher.find()) {
            //得到表达式中的内容，比如goods.sn
            String ex = matcher.group(1);
            try {
                // 得到表达式对应的值
                Object value = this.getValue(signature, args, ex);
                // 放到map中
                valuesMap.put(ex, value);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
        //替换表达式中的${}
        return this.resolvedDetail(detail, valuesMap);
    }

    /**
     * 得到参数中ex参数名的值
     *
     * @param signature point的signature
     * @param args point得到的注解方法上的所有参数数组
     * @param ex 表达式中的内容，比如goods.sn
     * @return
     * @throws IllegalArgumentException
     */
    private Object getValue(CodeSignature signature, Object[] args, String ex) throws IllegalArgumentException{
        String resultStr = ex;
        //将表达式内容拆分，得到key，比如 得到["goods","sn"]
        String[] keys = ex.split("\\.");
        //切点得到方法 所有的参数名
        String[] paramsName = signature.getParameterNames();
        //表达式拆分后的参数名，如goods
        String objectName = keys[0];
        // 查看表达式中的参数，在方法入参的所在位置
        int index = this.findArgPos(paramsName, objectName);
        // 参数中存在表达式的参数
        if (index != -1) {
            // 并且是 对象.属性格式
            if (keys.length == 2) {
                //得到表达式中值的名字，如sn
                String propertyName = keys[1];
                // 得到参数对象 比如Goods
                Object obj = args[index];
                // 将参数对象转成json
                JSONObject json = JSONObject.fromObject(obj);
                // json中取值
                Object value = json.get(propertyName);
                return value == null ? "" : value;
            } else {
                Object value = args[index];
                return value;
            }
        }
        return resultStr;
    }

    /**
     * 根据参数名，查找参数位置
     *
     * @param paramsName
     * @param objectName
     * @return
     */
    private int findArgPos(String[] paramsName, String objectName) {
        for (int i = 0; i < paramsName.length; i++) {
            if (paramsName[i].equals(objectName)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 将${}里面的内容替换相应的值
     *
     * @param templateString 需要替换${}的字符串
     * @param valuesMap      对应${}里的map
     * @return
     */
    private String resolvedDetail(String templateString, Map valuesMap) {
        StrSubstitutor sub = new StrSubstitutor(valuesMap);
        String resolvedString = sub.replace(templateString);
        return resolvedString;
    }



}
